import logging
from datetime import datetime, timedelta

import markdown
from bs4 import BeautifulSoup
from django.db import models
from django.db.models import QuerySet
from django.utils import timezone
from django.utils.timezone import utc
from mdeditor.fields import MDTextField
from PIL import Image

logger = logging.getLogger('myblog')


class BaseModel(models.Model):
    created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    modified = models.DateTimeField(auto_now=True, verbose_name='修改时间')

    class Meta:
        abstract = True  # 不会创建表


class Category(BaseModel):
    name = models.CharField(max_length=64, unique=True, verbose_name='类别名')

    class Meta:
        verbose_name = '类别'
        verbose_name_plural = '类别'
        ordering = ('-created', '-id')

    def __str__(self):
        return self.name


class Tag(BaseModel):
    name = models.CharField(max_length=64, unique=True, verbose_name='标签名')

    class Meta:
        verbose_name = '标签'
        verbose_name_plural = '标签'
        ordering = ('-created', '-id')

    def __str__(self):
        return self.name


class PostQuerySet(QuerySet):

    # 获取两周内浏览量最多的十篇文章
    # Post模型中pub_date存储的是带时区的UTC时间(timezone.now())
    # 所以这里不使用直接datetime.utcnow()
    def popular_posts(self):
        today = datetime.utcnow().replace(tzinfo=utc)
        two_weeks_ago = today - timedelta(days=14)
        two_weeks_posts = self.published().filter(created__gt=two_weeks_ago)
        return two_weeks_posts.order_by("-num_views")[:4]

    # 获得上一篇文章的slug,如果没有则返回None
    # def get_prev_post(self, current_post):
    #     prev_post = self.published().filter(created__lt=current_post.created).first()  # 空的查询集对象的first()方法返回None
    #     if prev_post is not None:
    #         return {'title': prev_post.title[:8] + '...', 'slug': prev_post.slug}

    # 获得下一篇文章的slug,如果没有则返回None
    def get_next_post(self, current_post):
        next_post = self.published().filter(created__gt=current_post.created).first()
        if next_post is not None:
            # return {'title': next_post.title[:8] + '...', 'slug': next_post.slug}
            return {'title': next_post.title, 'slug': next_post.slug}

    # 只显示`发表`状态的文章
    def published(self):
        return self.filter(status=self.model.PUBLISHED)

    # 获取文章状态为“发表”的文章查询集合
    def published_posts(self):
        return self.published().all()


# 默认类别为`其他`
def get_default_category():
    # get_or_create返回(object, created) created是布尔值，表示是否创建
    return Category.objects.get_or_create(name='其他')[0]


class Post(BaseModel):
    DRAFT = 'd'
    PUBLISHED = 'p'

    ARTICLE_STATUES = (
        (DRAFT, '草稿'),
        (PUBLISHED, '发表'),
    )

    title = models.CharField(max_length=100, unique=True, verbose_name='标题')
    # 使用标题作为slug,因为是单人应用避免使用相同的文章标题即可
    slug = models.CharField(
        max_length=100,
        default='',
        blank=True,  # 表单认证，不填默认保存为空串
        verbose_name='短标题'
    )

    subtitle = models.CharField(max_length=120, default='', blank=True, verbose_name='子标题')
    # content = models.TextField()
    content = MDTextField(verbose_name='正文')  # https://www.jianshu.com/p/442bc083c835

    # 重写父类created类属性，允许修改发表时间
    created = models.DateTimeField(default=timezone.now, verbose_name='创建时间')

    # 如果文章类别被删除，设置类别为`其他`,不要删除文章
    # 类别`其他`不能删！！
    category = models.ForeignKey(
        Category,
        on_delete=models.SET(get_default_category),
        verbose_name='分类',
        default=get_default_category   # 默认对象
    )

    # 允许标签为空
    tags = models.ManyToManyField(Tag, verbose_name='标签集合', blank=True)
    num_views = models.PositiveIntegerField(default=0, verbose_name='浏览量')
    status = models.CharField(max_length=1, choices=ARTICLE_STATUES, verbose_name='文章状态', default=DRAFT)

    # 如果配置了MEDIA_ROOT,图片会上传到media/images目录下
    img = models.ImageField("标题图", upload_to='images', null=True, blank=True)

    objects = PostQuerySet.as_manager()

    def __str__(self):
        return self.title[:20]

    class Meta:
        verbose_name = '文章'
        verbose_name_plural = '文章'
        ordering = ('-created', '-id')

    @property
    def pub_date(self):
        return self.created.strftime('%Y年%m月%d日 %H:%M')

    @property
    def img_path(self):
        # 获取不带域名的图片路径
        # "/media/images/beach.jpg",
        try:
            return self.img.url
        except (ValueError, AttributeError):
            return ''

    # 文章摘要-在文章列表页面显示
    @property
    def excerpt(self):
        # 把markdown格式文档转化为HTML文档
        self.content = markdown.markdown(
            self.content,
            extensions=[
                'markdown.extensions.extra',
                'markdown.extensions.codehilite',
                'markdown.extensions.toc',
            ]
        )
        # 文章摘要，不带HTML标签
        bs = BeautifulSoup(self.content, "html.parser")
        return bs.text[:120] + "..."

    # 重写save方法使用title作为slug
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = self.title

        super().save(*args, **kwargs)
        self.resize_image()

    def resize_image(self):
        # 修改图片大小
        try:
            width = 300
            # height = 400
            img = Image.open(self.img.path)
            (x, y) = img.size
            new_x = width
            # new_y = height
            # new_x = int(new_y * (x / y))
            # 等比例缩小
            new_y = int(new_x * (y / x))
            # 高分辨率缩小
            resized_image = img.resize((new_x, new_y), Image.ANTIALIAS)
            resized_image.save(self.img.path)

            # if img.height > 300 or img.width > 300:
            #     new_img = (300, 300)
            #     img.thumbnail(new_img)
            #     img.save(self.img.path)
        except FileNotFoundError:  # 图片可能被删除
            pass
            # logger.error('图片没有找到')
        except Exception as e:
            pass
            # logger.error('修改图片大小失败:', str(e))


class Link(models.Model):
    name = models.CharField(max_length=100)
    url = models.URLField()
    ordering = models.PositiveSmallIntegerField(default=1)

    def __str__(self):
        return '<Link name="%s" url="%s" ordering=%s>' % (self.name, str(self.url), self.ordering)
